`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2023/12/17 15:20:08
// Design Name: 
// Module Name: rv_imem
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

`include "msg_config.v"
`include "rv_config.v"

//2KB/bank *16 banks =32KB
module rv_imem#(parameter addr_width = 11)(
    input CLK,input RST,
    //Supervior --> DMEM
    input EBUS_SEL,

    //ISSUE --> DMEM
    input [31:0] PC,

    //EBUS --> DMEM
    input CS_EBUS,
    input WR_EBUS,
    input [`EBUS_ADDR_WIDTH-1:0] ADDR_EBUS,
    input [127:0] DIN_EBUS,
    //imem-->issue and IR reg
    output [31:0] DOUT
    );

//    wire [3:0] bank_cs;
    wire bank_wr;
    wire [127:0] dmem_din;
    wire [127:0] dmem_dout;
    wire [addr_width-1:0] bank_addr;
    wire [1:0] addr2_reg;

//    assign bank_cs=(EBUS_SEL==1'b1)?{4{CS_EBUS}}:1'b1;
    assign bank_wr=EBUS_SEL&WR_EBUS;
    assign dmem_din=DIN_EBUS;
    assign bank_addr=(EBUS_SEL==1'b1)?ADDR_EBUS:PC[addr_width-1+4:4];
    genvar i;
    generate
        for(i=0;i<`THREAD_NUM;i=i+1) begin
            sram #(.addr_width(addr_width),.data_width(32)) WMEM_BANK(
                CLK,1'b1,bank_wr,bank_addr,
                dmem_din[i*32+31:i*32],dmem_dout[i*32+31:i*32]);
        end
    endgenerate

    regw #(.WIDTH(2)) ADDR2_REG(CLK,RST,1'b1,PC[3:2],addr2_reg);

    mux4w #(.WIDTH(32)) DOUT_MUX(dmem_dout[0*32+31:0*32],
        dmem_dout[1*32+31:1*32],dmem_dout[2*32+31:2*32],dmem_dout[3*32+31:3*32],
        addr2_reg,DOUT);
    
endmodule

module rv_pc(
    input CLK,input RST,
    //EXTERN --> rv_pc
    // 1:running 0:CLR
    input EBUS_RV_RUN,
    //thread-->rv_pc
    input THREAD_ID_FOR_PC_VALID,
    input [1:0] THREAD_ID_FOR_PC,
    //EX-->ISSUE
    input [1:0] UPDATE_PC_THREAD_ID,
    input UPDATE_PC_VALID,
    input [31:0] UPDATE_PC,
    
    //rv_pc-->imem
    output [31:0] PC
);
    genvar i;
    wire [7:0] cur_thread_id;
    wire [32*`THREAD_NUM-1:0] all_pc;
    assign cur_thread_id=8'b11_10_01_00;
    generate
        for(i=0;i<`THREAD_NUM;i=i+1) begin
            pc_module PC_MODULE(CLK,RST,cur_thread_id[2*i+1:2*i],
            EBUS_RV_RUN, 
            THREAD_ID_FOR_PC_VALID,THREAD_ID_FOR_PC,
            UPDATE_PC_THREAD_ID,UPDATE_PC_VALID,UPDATE_PC,
            all_pc[i*32+31:i*32]
            );
        end
    endgenerate
    mux4w PC_MUX(all_pc[0*32+31:0*32],
    all_pc[1*32+31:1*32],all_pc[2*32+31:2*32],all_pc[3*32+31:3*32],
    THREAD_ID_FOR_PC,PC);
endmodule

module pc_module(
    input CLK,input RST,
    input [1:0] CUR_THREAD_ID,
    //EXTERN --> rv_pc
    // 1:running 0:CLR
    input EBUS_RV_RUN,
    //thread-->rv_pc
    input THREAD_ID_FOR_PC_VALID,
    input [1:0] THREAD_ID_FOR_PC,
    //EX-->ISSUE
    input [1:0] UPDATE_PC_THREAD_ID,
    input UPDATE_PC_VALID,
    input [31:0] UPDATE_PC,
    output [31:0] PC    
);
    reg [31:0] next_pc;
    wire [31:0] cur_pc;
    always @(*) begin
        if(EBUS_RV_RUN==1'b0) next_pc=32'b0;
        else begin
            if((CUR_THREAD_ID==THREAD_ID_FOR_PC)&(THREAD_ID_FOR_PC_VALID==1'b1))
                next_pc=cur_pc+32'd4;
            else begin
                if((CUR_THREAD_ID==UPDATE_PC_THREAD_ID)&(UPDATE_PC_VALID==1'b1))
                    next_pc=UPDATE_PC;
                else 
                    next_pc=cur_pc;
            end
        end
    end
    
    regw #(.WIDTH(32)) PC_REG(CLK,RST,1'b1,next_pc,cur_pc);
    assign PC=cur_pc;

endmodule